package cn.gwm.flink.streaming.strategy.multidimension;

import cn.gwm.flink.streaming.constant.ChargePredictFields;
import cn.gwm.flink.streaming.strategy.vehiclepredict.DwsStrConstant;
import cn.gwm.utils.HBaseDataSource;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.flink.api.common.state.MapState;
import redis.clients.jedis.JedisCluster;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * @author : GW00280745
 * @date : 2022/12/19 13:14
 * @description :
 */
public class MultiDimensionTimePredictObc implements Serializable {
    public static void main(String[] args) throws Exception{
        MultiDimensionTimePredictObc timePredict = new MultiDimensionTimePredictObc();
        HBaseDataSource.ins().getDataSource().getConnection();

        //慢充调试
        String vinStr = "LGWEEUA52NE202588";
        String timeStr = "1681297814000";
        String socStr = "41.2";
        String currStr = "-9.2";
        String tempStr = "20";
        String aoiid = "B023E05RPU";
        String poiid = "B0FFFCQXJB";
        String modelcode = "CC7000CG00ABEV";
        String bmsDcChrgConnect="0";

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("vin",vinStr);
        jsonObject.put("tid",timeStr);
        jsonObject.put("startChargeTime",timeStr);
        jsonObject.put("mapId","-999999");
        jsonObject.put("bms_soc",socStr);
        jsonObject.put("bms_batt_curr_rm",currStr);
        jsonObject.put("bms_rmc_module_temp_min",tempStr);
        jsonObject.put("model_code",modelcode);
        jsonObject.put("bms_heatreq","0");
        jsonObject.put("acopensts","0");
        jsonObject.put("aoiid",aoiid);
        jsonObject.put("poiid",poiid);
        jsonObject.put("bms_dc_chrg_connect",bmsDcChrgConnect);
        String predict = timePredict.predictV2(jsonObject);
        System.out.println(predict);
    }

    private String itemTimeStr = "item_time";
    private String endChargeTimeStr = "endchargetime";
    private String char3 = "#";
    private String innerSocStr = "bms_innersoc";
    private String endSocStr = "end_bms_soc";
    private String startIsNull = "start_is_null";
    private String deltaTime = "delta_time";

    private PhoenixInfo queryUtil = new PhoenixInfo();

    public String time(JSONObject line, JedisCluster socIntervalJedis) {
        try {
            String predict = predict(line);
            if (StringUtils.isBlank(predict)) {
                return null;
            }
            //智能充电模块-数据对接 格式  charge:status:{vin}:{startChargeTime}
            intellectCharge(line, socIntervalJedis, predict);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String time(MapState<String, String> latestEstimateState,
                       JSONObject line,
                       String chargeGroup,
                       JedisCluster socIntervalJedis) {
        try {
            String predict = predict(line);
            if (StringUtils.isBlank(predict)) {
                return null;
            }
            //配合各种state
            String[] split = predict.split(ChargePredictFields.ins().char3);
            latestEstimateState.put(ChargePredictFields.ins().stdFieldEstimateTime, split[0]);
            line.put(ChargePredictFields.ins().stdFieldEstimateTime, split[0]);
            latestEstimateState.put(ChargePredictFields.ins().stdFieldPredictTime, predict);
            line.put(ChargePredictFields.ins().stdFieldPredictTime, predict);
            latestEstimateState.put(chargeGroup, predict);
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, predict);
            //智能充电模块-数据对接 格式  charge:status:{vin}:{startChargeTime}
            intellectCharge(line, socIntervalJedis, predict);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public String timeV1(MapState<String, String> latestEstimateState,
                         JSONObject line,
                         String chargeGroup,
                         JedisCluster socIntervalJedis,
                         boolean firstTime) {
        try {
            line.put(ChargePredictFields.ins().firstTimeField,firstTime);
            String predict = predictV2(line);
            if (StringUtils.isBlank(predict)) {
                return null;
            }
            //配合各种state
            String[] split = predict.split(ChargePredictFields.ins().char3);
            latestEstimateState.put(ChargePredictFields.ins().stdFieldEstimateTime, split[0]);
            line.put(ChargePredictFields.ins().stdFieldEstimateTime, split[0]);

            //predict_time字段   原本格式= 2022-09-10 17:05:22  临时用它存储每次预测的过程
            String predictTime = predictTime(line, split[0]);
            latestEstimateState.put(ChargePredictFields.ins().stdFieldPredictTime, predictTime);
            line.put(ChargePredictFields.ins().stdFieldPredictTime, predictTime);

            latestEstimateState.put(chargeGroup, predict);
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, predict);
            //智能充电模块-数据对接 格式  charge:status:{vin}:{startChargeTime}
            intellectCharge(line, socIntervalJedis, predict);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private String redisPrefix = "charge:status:";

    private void intellectCharge(JSONObject line, JedisCluster socIntervalJedis, String predict) {
        try {
            if (socIntervalJedis == null){
                System.out.println("socIntervalJedis is null.please check： ");
                return;
            }
            //charge:status:{vin}:{startChargeTime}
            String startChargeTime = line.getString(ChargePredictFields.ins().srcFieldStartChargeTime);
            String vinVal = line.getString(ChargePredictFields.ins().srcFieldVin);
            String key = redisPrefix.concat(vinVal).concat(ChargePredictFields.ins().colon)
                    .concat(startChargeTime);
            String s = predict.split(ChargePredictFields.ins().char3)[0];
            socIntervalJedis.set(key, s);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String predict(JSONObject line) {
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        JSONObject startTimeJson = startPointsOfCurve(line, bmsSoc, false);
        if (startTimeJson == null) {
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, startIsNull);
            return null;
        }
        Long startTime = startTimeJson.getLong(itemTimeStr);
        Long endTime = startTimeJson.getLong(endChargeTimeStr);
        long totalTime = endTime - startTime;
        String endSoc = startTimeJson.getString(endSocStr);
        String concat = String.valueOf(bmsSoc).concat(char3).concat(endSoc).concat(char3);
        float innersocEnd = startTimeJson.getFloatValue(endSocStr);
        if (Math.abs(innersocEnd - endSocLimit)> DwsStrConstant.ins().num5){
            //默认预测到95
            //差的这一段需要进行补 而当前结束点的soc就是起点
//            List<JSONObject> list = endSoc(startTimeJson, innersocEnd)
//            for (JSONObject jsonObject : list) {
//                float bSoc = jsonObject.getFloatValue(innerSocStr)
//                concat = concat.concat(String.valueOf(bSoc)).concat(char3)
//                Long a = jsonObject.getLong(deltaTime)
//                totalTime += a
//            }
            JSONObject json = breakPointsOfCurve(line, innersocEnd);
            if (json != null){
                float bSoc = json.getFloatValue(endSocStr);
                concat = concat.concat(String.valueOf(bSoc)).concat(char3);
                startTime = json.getLong(itemTimeStr);
                endTime = json.getLong(endChargeTimeStr);
                long a = endTime - startTime;
                totalTime += a;
            }

        }
        int i = new BigDecimal(totalTime).divide(new BigDecimal(60 * 1000), 0, BigDecimal.ROUND_HALF_DOWN).intValue();
        String res = String.valueOf(i).concat(char3).concat(concat);
        String substring = res.substring(0, res.length() - 1);
        line.put(ChargePredictFields.ins().stdFieldPredictTimeV2,substring);
        return substring;
    }

    /**
     * 起点阶段补充策略
     * 根据起点 电流+温度 匹配某充电记录，获得该充电记录的起始阶段时间和稳定后的电流、温度
     * 根据获得的电流、温度，加上当前soc+10，匹配
     **/
    private String predictV2(JSONObject line) {
        String s = predictV3(line);
        if (StringUtils.isNotBlank(s)){
            line.put("flag","jisuan");
            return s;
        }
        //就是没有匹配到
        String defaultS = defaultObc.process(line);
        if (StringUtils.isNotBlank(defaultS)){
            return defaultS;
        }
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        Float bmsCurr = line.getFloat(ChargePredictFields.ins().srcFieldBmsBatterCurrRm);
        float totalTime = 0;
        if (true){
            if (Math.abs(bmsCurr)<DwsStrConstant.ins().num7){
                totalTime = (100-(bmsSoc+1))*12f;
            }else if (Math.abs(bmsCurr)< DwsStrConstant.ins().num20){
                totalTime = (100-bmsSoc)*6.2f;
            }
            if (totalTime<=0){
                totalTime = 15;
            }
        }
//        totalTime = (100-bmsSoc) * 6.2f
//        totalTime = -999999
        String concat = String.valueOf((int) totalTime).concat(char3).concat(bmsSoc.toString()).concat(char3).concat("100");
        line.put("flag","default");
        return concat;
    }
    DefaultObc defaultObc = new DefaultObc();
    MultiDimensionTimePredictDc v3= new MultiDimensionTimePredictDc();
    private String predictV3(JSONObject line) {
        boolean isDc = StringUtils.equalsIgnoreCase(line.getString(ChargePredictFields.ins().srcFieldBmsDcConnect), "1");
        if (isDc){
//            return predictV3Dc(line)
            //样本数据库匹配流程--快充
            return v3.predictV3Dc(line);
        }
        //样本数据库匹配流程--慢充
        String obc = obcV1(line);
        return obc;
    }
    private String obc(JSONObject line) {
        //样本数据库匹配流程--慢充
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        //起始阶段匹配
        JSONObject startStageJson = startStage(line,bmsSoc);
        if (startStageJson==null){
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, startIsNull);
            return null;
        }
        long ssTime = startStageTime(startStageJson);
        //起始阶段之后的匹配
        Float startTimeJson = startPointsOfCurve(line, startStageJson, bmsSoc);
        if (startTimeJson == null) {
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, startIsNull);
            return null;
        }
        //时间相加得出结果
        String concat = String.valueOf(bmsSoc).concat(char3).concat("99").concat(char3);
        int i = new BigDecimal(ssTime).divide(new BigDecimal(60 * 1000), 0, BigDecimal.ROUND_HALF_DOWN).intValue();
        i += startTimeJson;
        String res = String.valueOf(i).concat(char3).concat(concat);
        String substring = res.substring(0, res.length() - 1);
        line.put(ChargePredictFields.ins().stdFieldPredictTimeV2,substring);
        return substring;
    }
    private String obcV1(JSONObject line) {
        //样本数据库匹配流程--慢充
        //忽略起始阶段试试
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        //起始阶段之后的匹配
        Float startTimeJson = startPointsOfCurve(line, null, bmsSoc);
        if (startTimeJson == null) {
            line.put(ChargePredictFields.ins().stdFieldPredictTimeV2, startIsNull);
            return null;
        }
        //时间相加得出结果
        String concat = String.valueOf(bmsSoc).concat(char3).concat("99").concat(char3);
//        int i = new BigDecimal(ssTime).divide(new BigDecimal(60 * 1000), 0, BigDecimal.ROUND_HALF_DOWN).intValue()
        int i = 0;
        i += startTimeJson;
        String res = String.valueOf(i).concat(char3).concat(concat);
        String substring = res.substring(0, res.length() - 1);
        line.put(ChargePredictFields.ins().stdFieldPredictTimeV2,substring);
        return substring;
    }
    private long startStageTime(JSONObject startStageJson) {
        String steadyTime1 = startStageJson.getString("steady_time");
        String startChargeTime = startStageJson.getString("startChargeTime");
        long l = new BigDecimal(steadyTime1).subtract(new BigDecimal(startChargeTime)).longValue();
        return l;
    }

    private JSONObject startStage(JSONObject line,Float bmsSoc) {
        //1.先匹配soc和温度这两个字段
        String startConditionCurr = queryUtil.startQuery(line,bmsSoc,null,null);
        List<JSONObject> list = queryUtil.queryMapId2(startConditionCurr, line);
        //2.基于上一步结果匹配电流、地点维度
        JSONObject json= otherDimension(line,list);
        return json;
    }
    private JSONObject otherDimension(JSONObject line,List<JSONObject> list) {
       //将同一个地点 同一个vin 电流相同的记录找出
        if (list==null || list.size()==0){
            return null;
        }
        String vin = line.getString(vinKey);
        String curr = line.getString(currKey);
        float currV = 0f;
        try {
            currV = Float.parseFloat(curr);
        }catch (Exception e){e.printStackTrace();}
        String poiId = line.getString(ChargePredictFields.ins().srcFieldPoiId);
        String aoiId = line.getString(ChargePredictFields.ins().srcFieldAoiId);
        boolean oiIdB = StringUtils.isNotBlank(poiId) || StringUtils.isNotBlank(aoiId);
        //1.地点
        ArrayList<JSONObject> poiList = new ArrayList<>();
        ArrayList<JSONObject> currList = new ArrayList<>();
        for (JSONObject item : list) {
            String poiId1 = item.getString(ChargePredictFields.ins().srcFieldPoiId);
            String aoiId1 = item.getString(ChargePredictFields.ins().srcFieldAoiId);
            //地点是否相同
            if(oiIdB){
                if (StringUtils.equalsIgnoreCase(aoiId,aoiId1) || StringUtils.equalsIgnoreCase(poiId,poiId1)){
                    poiList.add(item);
                    continue;
                }
            }
            currList.add(item);
        }
        //地点list 即 有相同地点的数据
        if (poiList.size()>0){
            //找电流相同的
            ArrayList<JSONObject> vinList = new ArrayList<>();
            for (JSONObject item : poiList) {
                String battCurr = item.getString(currKey);
                Float battCurrV = 0f;
                try {
                    battCurrV = Float.parseFloat(battCurr);
                }catch (Exception e){e.printStackTrace();}

                Float abs = Math.abs(battCurrV - currV);
                if (abs>0 && abs<=5){
                    vinList.add(item);
                    continue;
                }
            }
            if (vinList.size()==0){
                return null;
            }
            //找vin相同的
            for (JSONObject item : vinList) {
                String deviceId = item.getString(deviceIdKey);
                if (StringUtils.equalsIgnoreCase(deviceId,vin)){
                    return item;
                }
            }
            //没有相同vin 选第一个
            JSONObject jsonObject = vinList.get(0);
            return jsonObject;
        }
        //2.没有相同的地点
        // 找找是否有相同的电流
        if (poiList.size()==0 && currList.size()>0){
            //找vin相同的
            for (JSONObject item : currList) {
                String deviceId = item.getString(deviceIdKey);
                if (StringUtils.equalsIgnoreCase(deviceId,vin)){
                    return item;
                }
            }
            //没有相同的vin 那就随机返回一个
            JSONObject jsonObject = currList.get(0);
            return jsonObject;
        }
        //3.地点、电流都没有相同的 那就返回null
        return null;
    }
    private JSONObject otherDimensionV2(JSONObject line,List<JSONObject> list) {
        //将同一个地点 同一个vin 电流相同的记录找出
        if (list==null || list.size()==0){
            return null;
        }
        String vin = line.getString(vinKey);
        String curr = line.getString(currKey);
        float currV = 0f;
        try {
            currV = Float.parseFloat(curr);
        }catch (Exception e){e.printStackTrace();}
        String poiId = line.getString("poiid");
        String aoiId = line.getString("aoiid");
        boolean oiIdB = StringUtils.isNotBlank(poiId) || StringUtils.isNotBlank(aoiId);
        //1.地点
        ArrayList<JSONObject> poiList = new ArrayList<>();
        ArrayList<JSONObject> currList = new ArrayList<>();
        for (JSONObject item : list) {
            String poiid = item.getString("poiid");
            String aoiid = item.getString("aoiid");
            //地点是否相同
            if(oiIdB){
                if (StringUtils.equalsIgnoreCase(aoiId,aoiid) || StringUtils.equalsIgnoreCase(poiId,poiid)){
                    poiList.add(item);
                    continue;
                }
            }
            currList.add(item);
        }
        //地点list 即 有相同地点的数据
        if (poiList.size()>0){
            //选第一个
            JSONObject jsonObject = poiList.get(0);
            return jsonObject;
        }
        //2.没有相同的地点
        JSONObject jsonObject = currList.get(0);
        return jsonObject;
    }
    private String steadyCurrKey="steady_curr";
    private String steadyTempMinKey="steady_tempmin";
    private Float startPointsOfCurve(JSONObject line, JSONObject startTimeJson,Float bmsSoc1){
        Object steadyCurr = null;
        Object steadyTempMin = null;
        if (startTimeJson!=null){
            steadyCurr = startTimeJson.get(steadyCurrKey);
            steadyTempMin = startTimeJson.get(steadyTempMinKey);
        }
//        float soc = bms_soc + 10
        float soc = bmsSoc1;
        //1.先匹配soc和温度这两个字段
        String startConditionCurr = queryUtil.startQuery(line,soc,steadyCurr,steadyTempMin);
        List<JSONObject> list = queryUtil.queryMapId2(startConditionCurr, line);
        //2.基于上一步结果匹配电流、地点维度
        JSONObject json= otherDimensionV2(line,list);
        if (json==null){
            return null;
        }
        String e = json.getString("e");
        if (StringUtils.isBlank(e)){
            return null;
        }
        float ef = Float.parseFloat(e);
        float v = ef * (100 - soc) ;
        return v;
    }
    private Float startPointsOfCurveV1(JSONObject line, JSONObject startTimeJson,Float bmsSoc){
        Object steadyCurr = null;
        Object steadyTempMin = null;
        if (startTimeJson!=null){
            steadyCurr = startTimeJson.get(steadyCurrKey);
            steadyTempMin = startTimeJson.get(steadyTempMinKey);
        }
        float soc = bmsSoc + 10;
        String startConditionCurr = queryUtil.startQueryV1(line,soc,steadyCurr,steadyTempMin);
//        Float v = queryUtil.queryMapId3(startConditionCurr, line, soc)
//        return v //64
        Float v = queryUtil.queryMapId4(startConditionCurr, line, soc);
//        return v // 68
        if (v==null){return v;}
        float testV = v * (100 - soc);
        //73
        return testV;
    }
    private String predictTime(JSONObject line, String deltaTime) {
        try {
            //预测时间单位=min
            long estiTime = new BigDecimal(deltaTime).longValue();
            long l = estiTime * 60 * 1000L;
            Long tid = line.getLong(ChargePredictFields.ins().srcFieldTid);
            long ls = tid + l;
            String format = DateFormatUtils.format(ls, ChargePredictFields.ins().yyyyMmddss1);
            return format;
        }catch (Exception e){
            return ChargePredictFields.ins().blankStr9;
        }
    }
    private float endSocLimit = 100f;

    private JSONObject startPointsOfCurve(JSONObject line, Float bmsSocF,boolean endSocFlag){
        JSONObject startTimeJson = null;
        if (true){
//            return startPointOfCurve(line,bms_soc,endSocFlag)
        }
        //如果没有相匹配的开始点 可能是 1.该辆车的第一次充电记录 2.就是没有对应的点
        //放大电流和地点条件 一般慢充有两种 一个是8A 一个是18A，放大电流条件那么就意味着都囊括进来了。同时将地点也放大
        String startConditionCurr = queryUtil.startObcCurr(line,bmsSocF,endSocFlag,false,5,false,false);
        startTimeJson = queryUtil.queryMapId(startConditionCurr,line);
        if (startTimeJson == null){
            //同一辆车
            startConditionCurr = queryUtil.startObcTemp(line,bmsSocF,endSocFlag,false,5,false,false);
            startTimeJson = queryUtil.query(startConditionCurr);
            if (startTimeJson == null){
                //查其他车的数据
//                startConditionCurr = queryUtil.startObcCurrVehicle(line,bms_soc,endSocFlag,false)
//                startTimeJson = queryUtil.queryMapId(startConditionCurr,line)
            }
        }
        //如果还是null 那可能是找不到了
        return startTimeJson;
    }
    private JSONObject breakPointsOfCurve(JSONObject line, Float bmsSoc){
        String startConditionCurr = queryUtil.startObcCurr(line,bmsSoc,false,false,5,false,true);
        JSONObject startTimeJson = queryUtil.queryMapId(startConditionCurr,line);
        return startTimeJson;
    }

    private String defaultStr = "-999999";
    private String innerSocKey = "bms_innersoc";
    private String modelCodeKey = "model_code";
    private String startChargeTimeKey = "startChargeTime";
    private String vinKey = "vin";
    private String deviceIdKey = "deviceId";
    private String tidKey = "tid";
    private String itemTimeKey = "item_time";
    private String mapIdKey = "mapId";
    private String currKey = "bms_batt_curr_rm";
    private String inletTempKey = "bms_inlettemps";
    private String heatReqKey = "bms_heatreq";
    private String acOpenKey = "acopensts";
    private String socLimKey = "tbox_batt_soc_lim";
    private String acAmbKey = "acambtemp";
    private String sohKey = "soh_cor";
    private String endTimeKey = "endchargetime";
    private String endSocKey = "end_bms_soc";
}
